/*!
  @file transporter_server.cpp
  @brief Brief description
  @author Kyohei Otsu <kyon@ac.jaxa.jp>
  @date April, 2013
*/

#include <arpa/inet.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#include "net/io.h"
#include "net/transporter.h"
#include "net/text_handler.h"
#include "core/core.h"

namespace net
{

int Transporter::startServer()
{
  if (server_running_) {
    WARN("Server already running");
    return -1;
  }

  // Set sockaddr_in
  memset(&addr_, 0, sizeof(addr_));
  addr_.sin_family = AF_INET;
  addr_.sin_port = htons(port_);
  addr_.sin_addr.s_addr = htonl(INADDR_ANY);

  int rc;

  // Create socket
  sock_ = socket(PF_INET, SOCK_STREAM, 0);
  if (sock_ < 0) {
    ERROR("Socket open");
    return -1;
  }

  // Bind
  rc = bind(sock_, (struct sockaddr *)&addr_, sizeof(addr_));
  if (rc < 0) {
    ERROR("Bind");
    return -1;
  }

  // Listen
  rc = listen(sock_, 3);
  if (rc < 0) {
    ERROR("Listen");
    close(sock_);
    return -1;
  }

  NETINFO(addr_, "TCP Server Started");
  server_running_ = true;
  return 0;
}

void Transporter::stopServer()
{
  shutdownAllConnections();
  if (!server_running_) return;
  server_running_ = false;
  INFO("Stopped Server");
}

void Transporter::shutdownAllConnections()
{
  INFO("Shutdown all connections");
  for (size_t i = 0; i < data_handlers_.size(); i++) 
    data_handlers_[i]->killThreads();
  data_handlers_.clear();
}

void Transporter::waitConnection()
{
  //-- init for select()
  fd_set fds;
  std::tr1::shared_ptr<struct timeval> timeout(new struct timeval); // loop ratio
  timeout->tv_sec = 5;
  timeout->tv_usec = 0;

  while (server_running_) {
    //-- set file descriptor
    FD_ZERO(&fds);
    FD_SET(sock_, &fds);

    //-- wait for accept
    int ready = select(sock_+1, &fds, NULL, NULL, timeout.get());
    if (errno == EINTR) break; // signal received
    else if (ready < 0) {
      WARN("select error");
      continue;
    }

    //-- accept new client
    if (FD_ISSET(sock_, &fds)) {
      struct sockaddr_in raddr;
      socklen_t raddr_len;
      int new_sock = accept(sock_, (struct sockaddr *) &raddr, &raddr_len);
      if (new_sock < 0) {
        ERROR("Accept failed");
        continue;
      }

      NETINFO(raddr, "accept new connection");

      // create data handler
      TextHandler::Ptr handler(new TextHandler(new_sock, "w+"));
      handler->startThreads();
      data_handlers_.push_back(handler);
    }

  }
}

} // end of namespace
